﻿using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.VisualStudio.Text;

namespace Language.Stub
{
    public partial class Comment : DetailedContext
    {
        public Comment()
        {
        }

        public Comment( comm.CommentStub source )
        {
            Set( source.context );
        }
    }

    public partial class Literal
    {
        public Literal()
        {
        }

        public Literal( comm.LiteralStub source )
        {
            Set( source.context );

            Type = (LiteralType)source.type;
        }
    }

    public partial class Enum
    {
        public Enum( comm.EnumStub source )
        {
            Set( source.context );
            Scope.Set( source.scope );

            Configuration.Set( source.flags );

            Add( source.options );
        }

        private void Add( IEnumerable<comm.EnumOptionStub> options )
        {
            foreach ( comm.EnumOptionStub source in options )
            {
                Option option = new Option( source ) { Parent = this };
                Options.Add( option );
            }
        }
    }

    public partial class Option
    {
        public Option( comm.EnumOptionStub source )
        {
            Set( source.context );

            Value = source.value;
        }
    }

    public partial class Property
    {
        public Property( comm.PropertyStub source )
        {
            Set( source.context );

            TypeDeclaration.Set( source.typeDeclarationContext );
            TypeDefinition.Set( source.typeDefinitionContext );

            Configuration.Set( source.flags );
        }
    }

    static class Properties
    {
        public static void Add( IList<Property> target, IEnumerable<comm.PropertyStub> source )
        {
            foreach ( comm.PropertyStub sourceStub in source )
            {
                Property property = new Property( sourceStub );
                target.Add( property );
            }
        }
    }

    static class Functions
    {
        public static void Add( IList<Function> target, IEnumerable<comm.FunctionStub> source, Class parent = null )
        {
            foreach( comm.FunctionStub sourceStub in source )
            {
                Function function = new Function( sourceStub, parent );
                target.Add( function );
            }
        }
    }

    public partial class Function
    {
        public Function( comm.FunctionStub source, Class parent )
            : this( source )
        {
            Parent = parent;
        }

        public Function( comm.FunctionStub source )
        {
            Set( source.context );
            Properties.Add( Locals, source.localsNew );
            Properties.Add( Parameters, source.parametersNew );

            TypeDeclaration.Set( source.typeDeclarationContext );
            TypeDefinition.Set( source.typeDefinitionContext );

            Scope.Set( source.scope );

            Configuration.Set( source.flags );
        }

        private Function( Function other )
        {
            Name = other.Name;
            Parent = other.Parent;
            Position = other.Position;
            File = other.File;
            Line = other.Line;
            LineStart = other.LineStart;

            Locals = other.Locals;

            if( other.Parameters.Count > 1 )
            {
                Parameters = other.Parameters.GetRange( 1, other.Parameters.Count - 1 );
            }
        }

        /// <summary>
        /// http://confluence:8090/display/CPT/Functions#Functions-Staticfunctionsinstructures:“syntaxshortcut”
        /// </summary>
        public static Function CreateStaticTransformed( Function source )
        {
            Debug.Assert( source.Configuration.IsStatic );
            Debug.Assert( source.Parent != null );
            Debug.Assert( source.Parameters.Count > 0 );
            Debug.Assert( source.Parameters[ 0 ].TypeDeclaration.Name == source.Parent.Name );

            Function function = new Function( source );
            return function;
        }
    }

    public partial class Class
    {
        public Class( comm.ClassStub source )
        {
            Set( source.context );
            Stub.Properties.Add( Properties, source.properties );
            Stub.Functions.Add( Functions, source.functions, this );

            TypeDeclaration.Set( source.extendsDeclarationContext );
            TypeDefinition.Set( source.extendsDefinitionContext );

            Scope.Set( source.scope );

            Configuration.Set( source.flags );
        }
    }

    public partial class File
    {
        public File( string path, ITextSnapshot snapshot )
        {
            Path = path;
            Snapshot = snapshot;
        }

        public void Add( Class stub )
        {
            Add( (DetailedContext)stub );

            foreach( Function function in stub.Functions )
            {
                Add( function, false );
            }

            foreach( Property property in stub.Properties )
            {
                Add( property, false );
            }

            AddScope( stub );
        }

        public void Add( Function stub, bool isTopLevel = true )
        {
            // Context
            Add( (DetailedContext)stub, isTopLevel );

            foreach( Property property in stub.Parameters )
            {
                Add( property, false );
            }

            foreach ( Property property in stub.Locals )
            {
                Add( property, false );
            }

            AddScope( stub );
        }

        public void Add( Enum stub, bool isTopLevel = true )
        {
            Add( (DetailedContext)stub, isTopLevel );

            foreach( Option option in stub.Options )
            {
                Add( (DetailedContext)option, false );
            }

            AddScope( stub );
        }

        public void Add( Literal stub )
        {
            Literals.Add( stub.Position.End, stub );
        }

        public void Add( DetailedContext context, bool isTopLevel = true )
        {
            // Context
            Add( (Context)context, isTopLevel );
            
            Stubs.Add( context.Position.End, context );
        }

        public void Add( Context context, bool isTopLevel = true )
        {
            if( isTopLevel )
            {
                // IFlattener
                Store( context, FlatStubs );

                // NameContext
                Store( context, NamedStubs );
            }
        }

        private void AddScope( IScope scope )
        {
            if( scope.Scope.Start.Position.Start != -1 )
                Scopes.Add( scope.Scope.End.Position.End, scope );
        }

        private static void Store<I>( Context stub, ICollection< I > storage ) where I : class
        {
            I i = stub as I;
            if ( i != null )
            {
                storage.Add( i );
            }
        }
    }
}
